using UnityEngine.Rendering;
using UnityEngine;
public partial class SSSSScatter
{

    enum Pass
    {
        DoScatter,
        ScatterCombine,
    }

    const string bufferName= "SSSSScatter";


    static ShaderTagId
    litShaderTagId = new ShaderTagId("SSSSSSMask");

    CommandBuffer buffer = new CommandBuffer
    {
        name = bufferName
    };


    ScriptableRenderContext context;

    Camera camera;

    int maskRT = default,
        blurRT1 = default,
        blurRT2 = default,
        src = default;

    CullingResults cullingResults;

    public bool IsActive => settings != null;

    Vector2Int bufferSize;

    private Material m_Material;
    public Shader shader;
    SSSSScatterSettings settings;
    bool useDynamicBatching;
    bool useGPUInstancing;
    bool useLightsPerObject;
    bool useHDR;
    int renderingLayerMask;
    Material material
    {
        get
        {
            if (m_Material == null && shader != null)
            {
                m_Material = new Material(shader);
                m_Material.hideFlags = HideFlags.HideAndDontSave;
            }
            return m_Material;
        }
    }

    public SSSSScatter()
    {
        shader = Shader.Find("Custom RP/SSSSScatter");

        maskRT = Shader.PropertyToID("_MaskTex");
        blurRT1 = Shader.PropertyToID("_CYSSSSSBlur1");
        blurRT2 = Shader.PropertyToID("_CYSSSSSBlur2");
        src = Shader.PropertyToID("_CYSSSSSSource");
    }

    public void Setup(ScriptableRenderContext context, CullingResults cullingResults, Camera camera, Vector2Int bufferSize, 
        SSSSScatterSettings settings, bool useHDR, bool useDynamicBatching, bool useGPUInstancing, bool useLightsPerObject, int renderingLayerMask)
    {
        this.context = context;
        this.cullingResults = cullingResults;
        this.camera = camera;
        this.bufferSize = bufferSize;

        this.settings = settings;
        this.useDynamicBatching = useDynamicBatching;
        this.useGPUInstancing = useGPUInstancing;
        this.useLightsPerObject = useLightsPerObject;
        this.renderingLayerMask = renderingLayerMask;
        this.useHDR = useHDR;
        this.shader = settings.ScatterShader ? settings.ScatterShader : this.shader;


        this.settings =
            camera.cameraType <= CameraType.SceneView ? settings : null;
        ApplySceneViewState();

        RenderScatterMask();
    }


    void Submit()
    {
        buffer.EndSample(bufferName);
        ExecuteBuffer();
        context.Submit();
    }

    void ExecuteBuffer()
    {
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
    }

    void DrawMaskRT(
    bool useDynamicBatching, bool useGPUInstancing, bool useLightsPerObject,
    int renderingLayerMask
)
    {
        PerObjectData lightsPerObjectFlags = useLightsPerObject ?
            PerObjectData.LightData | PerObjectData.LightIndices :
            PerObjectData.None;
        var sortingSettings = new SortingSettings(camera)
        {
            criteria = SortingCriteria.CommonOpaque
        };
        var drawingSettings = new DrawingSettings(
            litShaderTagId, sortingSettings
        )
        {
            enableDynamicBatching = useDynamicBatching,
            enableInstancing = useGPUInstancing,
            perObjectData =
                PerObjectData.ReflectionProbes |
                PerObjectData.Lightmaps | PerObjectData.ShadowMask |
                PerObjectData.LightProbe | PerObjectData.OcclusionProbe |
                PerObjectData.LightProbeProxyVolume |
                PerObjectData.OcclusionProbeProxyVolume |
                lightsPerObjectFlags
        };

        var filteringSettings = new FilteringSettings(
            RenderQueueRange.opaque, renderingLayerMask: (uint)renderingLayerMask
        );

        context.DrawRenderers(
            cullingResults, ref drawingSettings, ref filteringSettings
        );

        sortingSettings.criteria = SortingCriteria.CommonTransparent;
        drawingSettings.sortingSettings = sortingSettings;
        filteringSettings.renderQueueRange = RenderQueueRange.transparent;

        context.DrawRenderers(
            cullingResults, ref drawingSettings, ref filteringSettings
        );
    }

    void Cleanup()
    {
        buffer.ReleaseTemporaryRT(blurRT1);
        buffer.ReleaseTemporaryRT(blurRT2);
        buffer.ReleaseTemporaryRT(src);
        buffer.ReleaseTemporaryRT(maskRT);
    }

    void RenderScatterMask()
    {
        buffer.GetTemporaryRT(maskRT, bufferSize.x, bufferSize.y, 24, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
        buffer.SetRenderTarget(maskRT, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
        context.SetupCameraProperties(camera);
        buffer.ClearRenderTarget(false, true, Color.clear);

        buffer.BeginSample(bufferName);
        ExecuteBuffer();
        DrawMaskRT(useDynamicBatching, useGPUInstancing, useLightsPerObject, renderingLayerMask);

        Submit();
    }

    public void Render(int sourceId)
    {
        //buffer.BeginSample(bufferName);
        DoScatter(sourceId);

        //buffer.EndSample(bufferName);

        ExecuteBuffer();
        context.Submit();
        Cleanup();
    }

    void DoScatter(int sourceId)
    {


        int bufferWidth = (int)(bufferSize.x / this.settings.downscale),
            bufferHeight = (int)(bufferSize.y / this.settings.downscale);

        buffer.GetTemporaryRT(blurRT1, bufferWidth, bufferHeight, 16, FilterMode.Bilinear, RenderTextureFormat.ARGBFloat);
        buffer.GetTemporaryRT(blurRT2, bufferWidth, bufferHeight, 16, FilterMode.Bilinear, RenderTextureFormat.ARGBFloat);
        //buffer.GetTemporaryRT(src, bufferSize.x, bufferSize.y, 24, FilterMode.Bilinear, RenderTextureFormat.ARGBFloat);
        buffer.GetTemporaryRT(src, bufferSize.x, bufferSize.y,0, FilterMode.Bilinear, useHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default);

        buffer.SetGlobalFloat("_SoftDepthBias", this.settings.softDepthBias * 0.05f * 0.2f);

        buffer.Blit(sourceId, blurRT2);
        buffer.Blit(sourceId, src);

        //buffer.Blit(BuiltinRenderTextureType.CurrentActive, sourceBuf);
        //buffer.Blit(BuiltinRenderTextureType.CameraTarget, src);

        //multipass pass blur
        for (int k = 1; k <= this.settings.blurIterations; k++)
        {
            buffer.SetGlobalFloat("_BlurStr", Mathf.Clamp01(this.settings.scatterDistance * 0.08f - k * 0.022f * this.settings.scatterDistance));
            buffer.SetGlobalVector("_BlurVec", new Vector4(1, 0, 0, 0));
            buffer.Blit(blurRT2, blurRT1, material, (int)Pass.DoScatter);
            buffer.SetGlobalVector("_BlurVec", new Vector4(0, 1, 0, 0));
            buffer.Blit(blurRT1, blurRT2, material, (int)Pass.DoScatter);

            buffer.SetGlobalVector("_BlurVec", new Vector4(1, 1, 0, 0).normalized);
            buffer.Blit(blurRT2, blurRT1, material, (int)Pass.DoScatter);
            buffer.SetGlobalVector("_BlurVec", new Vector4(-1, 1, 0, 0).normalized);
            buffer.Blit(blurRT1, blurRT2, material, (int)Pass.DoScatter);
        }

        //buffer.Blit(blurRT2, BuiltinRenderTextureType.CameraTarget);
        //return;
        //buffer.Blit(blurRT2, blurBuf);

        buffer.SetGlobalTexture("_BlurTex", blurRT2);
        //buffer.SetGlobalTexture("_MainTex", src);
        buffer.SetGlobalFloat("_EffectStr", this.settings.scatterIntensity);
        buffer.SetGlobalFloat("_PreserveOriginal", 1 - this.settings.affectDirect);
        buffer.SetGlobalColor("_GloabScatteringColor", settings.scatteringColor);
        buffer.Blit(src, sourceId, material, (int)Pass.ScatterCombine);

    }


}
